<?php
declare(strict_types=1);
/**
 * Created by PhpStorm.
 * User: 我想有个家
 * Date: 2020/4/7
 * Time: 14:30
 */

namespace App\Middleware;


use App\Lib\Redis;

class Signatrue {
    private static $salt = 'hEi9qBDaMHtxOgY7';

    /**
     * @param \Psr\Http\Message\ServerRequestInterface $request
     * @return string 空表示验证通过
     */
    public static function check($request) {
        $header = $request->getHeaders();
        $server = [];

        $server['xx-deviceid'] = isset($header['xx-deviceid']) ? $header['xx-deviceid'][0] : '';
        if (empty($server['xx-deviceid'])) {
            return '签名参数错误';
        }

        $signature = isset($header['xx-signature']) ? $header['xx-signature'][0] : '';
        if (empty($signature)) {
            return '签名参数错误';
        }

        // 判断签名是否存在

        $server['xx-randomstr'] = isset($header['xx-randomstr']) ? $header['xx-randomstr'][0] : '';
        if (empty($server['xx-randomstr'])) {
            return '签名参数错误';
        }

        $server['xx-device'] = isset($header['xx-device']) ? $header['xx-device'][0] : '';
        if (empty($server['xx-device'])) {
            return '签名参数错误';
        }

        $server['xx-version'] = isset($header['xx-version']) ? $header['xx-version'][0] : '';
        if (empty($server['xx-version'])) {
            return '签名参数错误';
        }

        $server['xx-timestamp'] = isset($header['xx-timestamp']) ? $header['xx-timestamp'][0] : '';
        if (empty($server['xx-timestamp'])) {
            return '签名参数错误';
        }
        // 请求有效时间验证
        if (time() - $server['xx-timestamp'] > 60) {
            return '请求超时';
        }

        $server['xx-salt'] = self::$salt;
        // 验签
        if (self::verify($server, $signature)) {
            // 防止重放
            if (self::nonceCheck($signature)) {
                return '请求重复';
            }
            return '';
        } else {
            return '签名错误';
        }
    }

    /**
     * 参数校验
     * @param $param array 请求参数
     * @param $signatrue string 签名
     * @return bool
     */
    private static function verify($param, $signatrue) {
        ksort($param);
        $str = '';
        foreach ($param as $key => $val) {
            $val = trim($val, ' ');
            if ($val !== null && $val !== '') {
                $str .= "&{$key}={$val}";
            }
        }
        $str = trim($str, '&');
        return strtoupper(md5(sha1($str))) === $signatrue;
    }

    /**
     * 校验该签名是否存在
     * @param $str
     * @return bool
     */
    private static function nonceCheck($str) {
        $redis = Redis::get();
        if ($redis->get("za:{$str}")) {
            return true;
        }
        $redis->set("za:{$str}", 1, 60);
        return false;
    }
}
